#![doc = include_str!("../README.md")]

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Lit, Meta, MetaList, MetaNameValue, NestedMeta};

#[proc_macro_derive(XlsxGroupWriteQuicker, attributes(xlsx_group_write))]
pub fn derive_main(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    impl_trait(&ast)
}

fn parse_attrs(ast: &syn::DeriveInput) -> Vec<(String, String)> {
    ast.attrs.iter().find_map(|attr| {
        if attr.path.is_ident("xlsx_group_write") {
            attr.parse_meta().ok().map(|meta| {
                if let Meta::List(MetaList { nested, .. }) = meta {
                    let args: Vec<_> = nested
                        .iter()
                        .filter_map(|nested_meta| {
                            if let NestedMeta::Meta(Meta::NameValue(MetaNameValue {
                                path,
                                lit,
                                ..
                            })) = nested_meta
                            {
                                let name = path.get_ident().unwrap().to_string();
                                if path.is_ident("line_writer_simple") || 
                                   path.is_ident("group_maker") || 
                                   path.is_ident("template_simple") || 
                                   path.is_ident("output_file_name_simple") ||
                                   path.is_ident("output_file_name_add_date") ||
                                   path.is_ident("template_advance") ||
                                   path.is_ident("custom_info_adder") ||
                                   path.is_ident("line_writer_advance") ||
                                   path.is_ident("line_writer_advance_with_extra_arg") ||
                                   path.is_ident("output_file_name_advance") ||
                                   path.is_ident("extra_arg_type") ||
                                   path.is_ident("template_getter"){
                                    match lit {
                                        Lit::Str(lit_str) => Some((name, lit_str.value())),
                                        _ => panic!(
                                            "xlsx_group_write attribute err,must be:line_writer_simple \
                                            group_maker template_simple output_file_name_simple \
                                            output_file_name_add_date template_advance custom_info_adder \
                                            line_writer_advance output_file_name_advance template_getter \
                                            extra_arg_type line_writer_advance_with_extra_arg" 
                                        ),
                                    }
                                } else {
                                    None
                                }
                            } else {
                                None
                            }
                        })
                        .collect();
                    args
                } else {
                    panic!("xlsx_group_write attribute err")
                }
            })
        } else {
            None
        }
    }).unwrap_or_default()
}

/// 实现 XlsxGroupWrite
fn impl_trait(ast: &syn::DeriveInput) -> TokenStream {
    let struct_name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = &ast.generics.split_for_impl();
    let attrs = parse_attrs(ast);
    // 行写入方法配置
    let line_writer_code = code::get_line_writer_code(&attrs);
    // 快捷版本分组id获取器
    let group_maker_code = code::get_group_make_code(&attrs);
    // 快捷模板
    let template_code = code::get_template_code(&attrs);
    // 个性化信息添加器
    let custom_info_adder_code = code::get_custom_info_adder_code(&attrs);
    // 输出文件名称
    let output_file_name_code = code::get_output_file_name_code(&attrs);
    // 额外的个性化参数
    let extra_arg_type_code = code::get_extra_arg_type_code(&attrs);
    quote! {
        impl #impl_generics XlsxGroupWrite for #struct_name #ty_generics #where_clause {
            #extra_arg_type_code
            #line_writer_code
            #group_maker_code
            #template_code
            #output_file_name_code
            #custom_info_adder_code
        }
    }
    .into()
}

mod code;
